home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / language / harvest.cpt / Harvest C / Tcl 6.2 / tclHistory.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-12  |  29.9 KB  |  1,098 lines

  1. #ifdef macintosh
  2. #    pragma segment tclHistory
  3. #endif
  4.  
  5. /* 
  6.  * tclHistory.c --
  7.  *
  8.  *    This module implements history as an optional addition to Tcl.
  9.  *    It can be called to record commands ("events") before they are
  10.  *    executed, and it provides a command that may be used to perform
  11.  *    history substitutions.
  12.  *
  13.  * Copyright 1990-1991 Regents of the University of California
  14.  * Permission to use, copy, modify, and distribute this
  15.  * software and its documentation for any purpose and without
  16.  * fee is hereby granted, provided that the above copyright
  17.  * notice appear in all copies.  The University of California
  18.  * makes no representations about the suitability of this
  19.  * software for any purpose.  It is provided "as is" without
  20.  * express or implied warranty.
  21.  */
  22.  
  23. #ifndef lint
  24. static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclHistory.c,v 1.23 91/10/28 09:11:16 ouster Exp $ SPRITE (Berkeley)";
  25. #endif /* not lint */
  26.  
  27. #include "tclInt.h"
  28.  
  29. /*
  30.  * This history stuff is mostly straightforward, except for one thing
  31.  * that makes everything very complicated.  Suppose that the following
  32.  * commands get executed:
  33.  *    echo foo
  34.  *    history redo
  35.  * It's important that the history event recorded for the second command
  36.  * be "echo foo", not "history redo".  Otherwise, if another "history redo"
  37.  * command is typed, it will result in infinite recursions on the
  38.  * "history redo" command.  Thus, the actual recorded history must be
  39.  *    echo foo
  40.  *    echo foo
  41.  * To do this, the history command revises recorded history as part of
  42.  * its execution.  In the example above, when "history redo" starts
  43.  * execution, the current event is "history redo", but the history
  44.  * command arranges for the current event to be changed to "echo foo".
  45.  *
  46.  * There are three additional complications.  The first is that history
  47.  * substitution may only be part of a command, as in the following
  48.  * command sequence:
  49.  *    echo foo bar
  50.  *    echo [history word 3]
  51.  * In this case, the second event should be recorded as "echo bar".  Only
  52.  * part of the recorded event is to be modified.  Fortunately, Tcl_Eval
  53.  * helps with this by recording (in the evalFirst and evalLast fields of
  54.  * the intepreter) the location of the command being executed, so the
  55.  * history module can replace exactly the range of bytes corresponding
  56.  * to the history substitution command.
  57.  *
  58.  * The second complication is that there are two ways to revise history:
  59.  * replace a command, and replace the result of a command.  Consider the
  60.  * two examples below:
  61.  *    format {result is %d} $num       |    format {result is %d} $num
  62.  *    print [history redo]           |    print [history word 3]
  63.  * Recorded history for these two cases should be as follows:
  64.  *    format {result is %d} $num       |    format {result is %d} $num
  65.  *    print [format {result is %d} $num] |    print $num
  66.  * In the left case, the history command was replaced with another command
  67.  * to be executed (the brackets were retained), but in the case on the
  68.  * right the result of executing the history command was replaced (i.e.
  69.  * brackets were replaced too).
  70.  *
  71.  * The third complication is that there could potentially be many
  72.  * history substitutions within a single command, as in:
  73.  *    echo [history word 3] [history word 2]
  74.  * There could even be nested history substitutions, as in:
  75.  *    history subs abc [history word 2]
  76.  * If history revisions were made immediately during each "history" command
  77.  * invocations, it would be very difficult to produce the correct cumulative
  78.  * effect from several substitutions in the same command.  To get around
  79.  * this problem, the actual history revision isn't made during the execution
  80.  * of the "history" command.  Information about the changes is just recorded,
  81.  * in xxx records, and the actual changes are made during the next call to
  82.  * Tcl_RecordHistory (when we know that execution of the previous command
  83.  * has finished).
  84.  */
  85.  
  86. /*
  87.  * Default space allocation for command strings:
  88.  */
  89.  
  90. #define INITIAL_CMD_SIZE 40
  91.  
  92. /*
  93.  * Forward declarations for procedures defined later in this file:
  94.  */
  95.  
  96. static void        DoRevs _ANSI_ARGS_((Interp *iPtr));
  97. static HistoryEvent *    GetEvent _ANSI_ARGS_((Interp *iPtr, char *string));
  98. static char *        GetWords _ANSI_ARGS_((Interp *iPtr, char *command,
  99.                 char *words));
  100. static void        InsertRev _ANSI_ARGS_((Interp *iPtr,
  101.                 HistoryRev *revPtr));
  102. static void        MakeSpace _ANSI_ARGS_((HistoryEvent *hPtr, int size));
  103. static void        RevCommand _ANSI_ARGS_((Interp *iPtr, char *string));
  104. static void        RevResult _ANSI_ARGS_((Interp *iPtr, char *string));
  105. static int        SubsAndEval _ANSI_ARGS_((Interp *iPtr, char *cmd,
  106.                 char *old, char *_new));
  107.  
  108. /*
  109.  *----------------------------------------------------------------------
  110.  *
  111.  * Tcl_InitHistory --
  112.  *
  113.  *    Initialize history-related state in an interpreter.
  114.  *
  115.  * Results:
  116.  *    None.
  117.  *
  118.  * Side effects:
  119.  *    History info is initialized in iPtr.
  120.  *
  121.  *----------------------------------------------------------------------
  122.  */
  123.  
  124. void
  125. Tcl_InitHistory(interp)
  126.     Tcl_Interp *interp;        /* Interpreter to initialize. */
  127. {
  128.     register Interp *iPtr = (Interp *) interp;
  129.     int i;
  130.  
  131.     if (iPtr->numEvents != 0) {
  132.     return;
  133.     }
  134.     iPtr->numEvents = 20;
  135.     iPtr->events = (HistoryEvent *)
  136.         ckalloc((unsigned) (iPtr->numEvents * sizeof(HistoryEvent)));
  137.     for (i = 0; i < iPtr->numEvents; i++) {
  138.     iPtr->events[i].command = (char *) ckalloc(INITIAL_CMD_SIZE);
  139.     *iPtr->events[i].command = 0;
  140.     iPtr->events[i].bytesAvl = INITIAL_CMD_SIZE;
  141.     }
  142.     iPtr->curEvent = 0;
  143.     iPtr->curEventNum = 0;
  144.     Tcl_CreateCommand((Tcl_Interp *) iPtr, "history", Tcl_HistoryCmd,
  145.         (ClientData) NULL, (void (*)()) NULL);
  146. }
  147.  
  148. /*
  149.  *----------------------------------------------------------------------
  150.  *
  151.  * Tcl_RecordAndEval --
  152.  *
  153.  *    This procedure adds its command argument to the current list of
  154.  *    recorded events and then executes the command by calling Tcl_Eval.
  155.  *
  156.  * Results:
  157.  *    The return value is a standard Tcl return value, the result of
  158.  *    executing cmd.
  159.  *
  160.  * Side effects:
  161.  *    The command is recorded and executed.  In addition, pending history
  162.  *    revisions are carried out, and information is set up to enable
  163.  *    Tcl_Eval to identify history command ranges.  This procedure also
  164.  *    initializes history information for the interpreter, if it hasn't
  165.  *    already been initialized.
  166.  *
  167.  *----------------------------------------------------------------------
  168.  */
  169.  
  170. int
  171. Tcl_RecordAndEval(interp, cmd, flags)
  172.     Tcl_Interp *interp;        /* Token for interpreter in which command
  173.                  * will be executed. */
  174.     char *cmd;            /* Command to record. */
  175.     int flags;            /* Additional flags to pass to Tcl_Eval. 
  176.                  * TCL_NO_EVAL means only record: don't
  177.                  * execute command. */
  178. {
  179.     register Interp *iPtr = (Interp *) interp;
  180.     register HistoryEvent *eventPtr;
  181.     int length, result;
  182.  
  183.     if (iPtr->numEvents == 0) {
  184.     Tcl_InitHistory(interp);
  185.     }
  186.     DoRevs(iPtr);
  187.  
  188.     /*
  189.      * Don't record empty commands.
  190.      */
  191.  
  192.     while (isspace(*cmd)) {
  193.     cmd++;
  194.     }
  195.     if (*cmd == '\0') {
  196.     Tcl_ResetResult(interp);
  197.     return TCL_OK;
  198.     }
  199.  
  200.     iPtr->curEventNum++;
  201.     iPtr->curEvent++;
  202.     if (iPtr->curEvent >= iPtr->numEvents) {
  203.     iPtr->curEvent = 0;
  204.     }
  205.     eventPtr = &iPtr->events[iPtr->curEvent];
  206.  
  207.     /*
  208.      * Chop off trailing newlines before recording the command.
  209.      */
  210.  
  211.     length = strlen(cmd);
  212.     while (cmd[length-1] == '\n') {
  213.     length--;
  214.     }
  215.     MakeSpace(eventPtr, length + 1);
  216.     strncpy(eventPtr->command, cmd, length);
  217.     eventPtr->command[length] = 0;
  218.  
  219.     /*
  220.      * Execute the command.  Note: history revision isn't possible after
  221.      * a nested call to this procedure, because the event at the top of
  222.      * the history list no longer corresponds to what's going on when
  223.      * a nested call here returns.  Thus, must leave history revision
  224.      * disabled when we return.
  225.      */
  226.  
  227.     result = TCL_OK;
  228.     if (flags != TCL_NO_EVAL) {
  229.     iPtr->historyFirst = cmd;
  230.     iPtr->revDisables = 0;
  231.     result = Tcl_Eval(interp, cmd, flags | TCL_RECORD_BOUNDS,
  232.         (char **) NULL);
  233.     }
  234.     iPtr->revDisables = 1;
  235.     return result;
  236. }
  237.  
  238. /*
  239.  *----------------------------------------------------------------------
  240.  *
  241.  * Tcl_HistoryCmd --
  242.  *
  243.  *    This procedure is invoked to process the "history" Tcl command.
  244.  *    See the user documentation for details on what it does.
  245.  *
  246.  * Results:
  247.  *    A standard Tcl result.
  248.  *
  249.  * Side effects:
  250.  *    See the user documentation.
  251.  *
  252.  *----------------------------------------------------------------------
  253.  */
  254.  
  255.     /* ARGSUSED */
  256. int
  257. Tcl_HistoryCmd(dummy, interp, argc, argv)
  258.     ClientData dummy;            /* Not used. */
  259.     Tcl_Interp *interp;            /* Current interpreter. */
  260.     int argc;                /* Number of arguments. */
  261.     char **argv;            /* Argument strings. */
  262. {
  263.     register Interp *iPtr = (Interp *) interp;
  264.     register HistoryEvent *eventPtr;
  265.     int length;
  266.     char c;
  267.  
  268.     /*
  269.      * If no arguments, treat the same as "history info".
  270.      */
  271.  
  272.     if (argc == 1) {
  273.     goto infoCmd;
  274.     }
  275.  
  276.     c = argv[1][0];
  277.     length = strlen(argv[1]);
  278.  
  279.     if ((c == 'a') && (strncmp(argv[1], "add", length)) == 0) {
  280.     if ((argc != 3) && (argc != 4)) {
  281.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  282.             " add event ?exec?\"", (char *) NULL);
  283.         return TCL_ERROR;
  284.     }
  285.     if (argc == 4) {
  286.         if (strncmp(argv[3], "exec", strlen(argv[3])) != 0) {
  287.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  288.             "\": should be \"exec\"", (char *) NULL);
  289.         return TCL_ERROR;
  290.         }
  291.         return Tcl_RecordAndEval(interp, argv[2], 0);
  292.     }
  293.     return Tcl_RecordAndEval(interp, argv[2], TCL_NO_EVAL);
  294.     } else if ((c == 'c') && (strncmp(argv[1], "change", length)) == 0) {
  295.     if ((argc != 3) && (argc != 4)) {
  296.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  297.             " change newValue ?event?\"", (char *) NULL);
  298.         return TCL_ERROR;
  299.     }
  300.     if (argc == 3) {
  301.         eventPtr = &iPtr->events[iPtr->curEvent];
  302.         iPtr->revDisables += 1;
  303.         while (iPtr->revPtr != NULL) {
  304.         HistoryRev *nextPtr;
  305.  
  306.         ckfree(iPtr->revPtr->newBytes);
  307.         nextPtr = iPtr->revPtr->nextPtr;
  308.         ckfree((char *) iPtr->revPtr);
  309.         iPtr->revPtr = nextPtr;
  310.         }
  311.     } else {
  312.         eventPtr = GetEvent(iPtr, argv[3]);
  313.         if (eventPtr == NULL) {
  314.         return TCL_ERROR;
  315.         }
  316.     }
  317.     MakeSpace(eventPtr, strlen(argv[2]) + 1);
  318.     strcpy(eventPtr->command, argv[2]);
  319.     return TCL_OK;
  320.     } else if ((c == 'e') && (strncmp(argv[1], "event", length)) == 0) {
  321.     if (argc > 3) {
  322.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  323.             " event ?event?\"", (char *) NULL);
  324.         return TCL_ERROR;
  325.     }
  326.     eventPtr = GetEvent(iPtr, argc==2 ? "-1" : argv[2]);
  327.     if (eventPtr == NULL) {
  328.         return TCL_ERROR;
  329.     }
  330.     RevResult(iPtr, eventPtr->command);
  331.     Tcl_SetResult(interp, eventPtr->command, TCL_VOLATILE);
  332.     return TCL_OK;
  333.     } else if ((c == 'i') && (strncmp(argv[1], "info", length)) == 0) {
  334.     int count, indx, i;
  335.     char *newline;
  336.  
  337.     if ((argc != 2) && (argc != 3)) {
  338.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  339.             " info ?count?\"", (char *) NULL);
  340.         return TCL_ERROR;
  341.     }
  342.     infoCmd:
  343.     if (argc == 3) {
  344.         if (Tcl_GetInt(interp, argv[2], &count) != TCL_OK) {
  345.         return TCL_ERROR;
  346.         }
  347.         if (count > iPtr->numEvents) {
  348.         count = iPtr->numEvents;
  349.         }
  350.     } else {
  351.         count = iPtr->numEvents;
  352.     }
  353.     newline = "";
  354.     for (i = 0, indx = iPtr->curEvent + 1 + iPtr->numEvents - count;
  355.         i < count; i++, indx++) {
  356.         char *cur, *next, savedChar;
  357.         char serial[20];
  358.  
  359.         if (indx >= iPtr->numEvents) {
  360.         indx -= iPtr->numEvents;
  361.         }
  362.         cur = iPtr->events[indx].command;
  363.         if (*cur == '\0') {
  364.         continue;        /* No command recorded here. */
  365.         }
  366.         sprintf(serial, "%6d  ", iPtr->curEventNum + 1 - (count - i));
  367.         Tcl_AppendResult(interp, newline, serial, (char *) NULL);
  368.         newline = "\n";
  369.  
  370.         /*
  371.          * Tricky formatting here:  for multi-line commands, indent
  372.          * the continuation lines.
  373.          */
  374.  
  375.         while (1) {
  376.         next = strchr(cur, '\n');
  377.         if (next == NULL) {
  378.             break;
  379.         }
  380.         next++;
  381.         savedChar = *next;
  382.         *next = 0;
  383.         Tcl_AppendResult(interp, cur, "\t", (char *) NULL);
  384.         *next = savedChar;
  385.         cur = next;
  386.         }
  387.         Tcl_AppendResult(interp, cur, (char *) NULL);
  388.     }
  389.     return TCL_OK;
  390.     } else if ((c == 'k') && (strncmp(argv[1], "keep", length)) == 0) {
  391.     int count, i, src;
  392.     HistoryEvent *events;
  393.  
  394.     if (argc != 3) {
  395.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  396.             " keep number\"", (char *) NULL);
  397.         return TCL_ERROR;
  398.     }
  399.     if (Tcl_GetInt(interp, argv[2], &count) != TCL_OK) {
  400.         return TCL_ERROR;
  401.     }
  402.     if ((count <= 0) || (count > 1000)) {
  403.         Tcl_AppendResult(interp, "illegal keep count \"", argv[2],
  404.             "\"", (char *) NULL);
  405.         return TCL_ERROR;
  406.     }
  407.  
  408.     /*
  409.      * Create a _new history array and copy as much existing history
  410.      * as possible from the old array.
  411.      */
  412.  
  413.     events = (HistoryEvent *)
  414.         ckalloc((unsigned) (count * sizeof(HistoryEvent)));
  415.     if (count < iPtr->numEvents) {
  416.         src = iPtr->curEvent + 1 - count;
  417.         if (src < 0) {
  418.         src += iPtr->numEvents;
  419.         }
  420.     } else {
  421.         src = iPtr->curEvent + 1;
  422.     }
  423.     for (i = 0; i < count; i++, src++) {
  424.         if (src >= iPtr->numEvents) {
  425.         src = 0;
  426.         }
  427.         if (i < iPtr->numEvents) {
  428.         events[i] = iPtr->events[src];
  429.         iPtr->events[src].command = NULL;
  430.         } else {
  431.         events[i].command = (char *) ckalloc(INITIAL_CMD_SIZE);
  432.         events[i].command[0] = 0;
  433.         events[i].bytesAvl = INITIAL_CMD_SIZE;
  434.         }
  435.     }
  436.  
  437.     /*
  438.      * Throw away everything left in the old history array, and
  439.      * substitute the _new one for the old one.
  440.      */
  441.  
  442.     for (i = 0; i < iPtr->numEvents; i++) {
  443.         if (iPtr->events[i].command != NULL) {
  444.         ckfree(iPtr->events[i].command);
  445.         }
  446.     }
  447.     ckfree((char *) iPtr->events);
  448.     iPtr->events = events;
  449.     if (count < iPtr->numEvents) {
  450.         iPtr->curEvent = count-1;
  451.     } else {
  452.         iPtr->curEvent = iPtr->numEvents-1;
  453.     }
  454.     iPtr->numEvents = count;
  455.     return TCL_OK;
  456.     } else if ((c == 'n') && (strncmp(argv[1], "nextid", length)) == 0) {
  457.     if (argc != 2) {
  458.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  459.             " nextid\"", (char *) NULL);
  460.         return TCL_ERROR;
  461.     }
  462.     sprintf(iPtr->result, "%d", iPtr->curEventNum+1);
  463.     return TCL_OK;
  464.     } else if ((c == 'r') && (strncmp(argv[1], "redo", length)) == 0) {
  465.     if (argc > 3) {
  466.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  467.             " redo ?event?\"", (char *) NULL);
  468.         return TCL_ERROR;
  469.     }
  470.     eventPtr = GetEvent(iPtr, argc==2 ? "-1" : argv[2]);
  471.     if (eventPtr == NULL) {
  472.         return TCL_ERROR;
  473.     }
  474.     RevCommand(iPtr, eventPtr->command);
  475.     return Tcl_Eval(interp, eventPtr->command, 0, (char **) NULL);
  476.     } else if ((c == 's') && (strncmp(argv[1], "substitute", length)) == 0) {
  477.     if ((argc > 5) || (argc < 4)) {
  478.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  479.             " substitute old _new ?event?\"", (char *) NULL);
  480.         return TCL_ERROR;
  481.     }
  482.     eventPtr = GetEvent(iPtr, argc==4 ? "-1" : argv[4]);
  483.     if (eventPtr == NULL) {
  484.         return TCL_ERROR;
  485.     }
  486.     return SubsAndEval(iPtr, eventPtr->command, argv[2], argv[3]);
  487.     } else if ((c == 'w') && (strncmp(argv[1], "words", length)) == 0) {
  488.     char *words;
  489.  
  490.     if ((argc != 3) && (argc != 4)) {
  491.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  492.             " words num-num/pat ?event?\"", (char *) NULL);
  493.         return TCL_ERROR;
  494.     }
  495.     eventPtr = GetEvent(iPtr, argc==3 ? "-1" : argv[3]);
  496.     if (eventPtr == NULL) {
  497.         return TCL_ERROR;
  498.     }
  499.     words = GetWords(iPtr, eventPtr->command, argv[2]);
  500.     if (words == NULL) {
  501.         return TCL_ERROR;
  502.     }
  503.     RevResult(iPtr, words);
  504.     iPtr->result = words;
  505.     iPtr->freeProc = (Tcl_FreeProc *) free;
  506.     return TCL_OK;
  507.     }
  508.  
  509.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  510.         "\": must be add, change, event, info, keep, nextid, ",
  511.         "redo, substitute, or words", (char *) NULL);
  512.     return TCL_ERROR;
  513. }
  514.  
  515. /*
  516.  *----------------------------------------------------------------------
  517.  *
  518.  * MakeSpace --
  519.  *
  520.  *    Given a history event, make sure it has enough space for
  521.  *    a string of a given length (enlarge the string area if
  522.  *    necessary).
  523.  *
  524.  * Results:
  525.  *    None.
  526.  *
  527.  * Side effects:
  528.  *    More memory may get allocated.
  529.  *
  530.  *----------------------------------------------------------------------
  531.  */
  532.  
  533. static void
  534. MakeSpace(hPtr, size)
  535.     HistoryEvent *hPtr;
  536.     int size;            /* # of bytes needed in hPtr. */
  537. {
  538.     if (hPtr->bytesAvl < size) {
  539.     ckfree(hPtr->command);
  540.     hPtr->command = (char *) ckalloc((unsigned) size);
  541.     hPtr->bytesAvl = size;
  542.     }
  543. }
  544.  
  545. /*
  546.  *----------------------------------------------------------------------
  547.  *
  548.  * InsertRev --
  549.  *
  550.  *    Add a _new revision to the list of those pending for iPtr.
  551.  *    Do it in a way that keeps the revision list sorted in
  552.  *    increasing order of firstIndex.  Also, eliminate revisions
  553.  *    that are subsets of other revisions.
  554.  *
  555.  * Results:
  556.  *    None.
  557.  *
  558.  * Side effects:
  559.  *    RevPtr is added to iPtr's revision list.
  560.  *
  561.  *----------------------------------------------------------------------
  562.  */
  563.  
  564. static void
  565. InsertRev(iPtr, revPtr)
  566.     Interp *iPtr;            /* Interpreter to use. */
  567.     register HistoryRev *revPtr;    /* Revision to add to iPtr's list. */
  568. {
  569.     register HistoryRev *curPtr;
  570.     register HistoryRev *prevPtr;
  571.  
  572.     for (curPtr = iPtr->revPtr, prevPtr = NULL; curPtr != NULL;
  573.         prevPtr = curPtr, curPtr = curPtr->nextPtr) {
  574.     /*
  575.      * If this revision includes the _new one (or vice versa) then
  576.      * just eliminate the one that is a subset of the other.
  577.      */
  578.  
  579.     if ((revPtr->firstIndex <= curPtr->firstIndex)
  580.         && (revPtr->lastIndex >= curPtr->firstIndex)) {
  581.         curPtr->firstIndex = revPtr->firstIndex;
  582.         curPtr->lastIndex = revPtr->lastIndex;
  583.         curPtr->newSize = revPtr->newSize;
  584.         ckfree(curPtr->newBytes);
  585.         curPtr->newBytes = revPtr->newBytes;
  586.         ckfree((char *) revPtr);
  587.         return;
  588.     }
  589.     if ((revPtr->firstIndex >= curPtr->firstIndex)
  590.         && (revPtr->lastIndex <= curPtr->lastIndex)) {
  591.         ckfree(revPtr->newBytes);
  592.         ckfree((char *) revPtr);
  593.         return;
  594.     }
  595.  
  596.     if (revPtr->firstIndex < curPtr->firstIndex) {
  597.         break;
  598.     }
  599.     }
  600.  
  601.     /*
  602.      * Insert revPtr just after prevPtr.
  603.      */
  604.  
  605.     if (prevPtr == NULL) {
  606.     revPtr->nextPtr = iPtr->revPtr;
  607.     iPtr->revPtr = revPtr;
  608.     } else {
  609.     revPtr->nextPtr = prevPtr->nextPtr;
  610.     prevPtr->nextPtr = revPtr;
  611.     }
  612. }
  613.  
  614. /*
  615.  *----------------------------------------------------------------------
  616.  *
  617.  * RevCommand --
  618.  *
  619.  *    This procedure is invoked by the "history" command to record
  620.  *    a command revision.  See the comments at the beginning of the
  621.  *    file for more information about revisions.
  622.  *
  623.  * Results:
  624.  *    None.
  625.  *
  626.  * Side effects:
  627.  *    Revision information is recorded.
  628.  *
  629.  *----------------------------------------------------------------------
  630.  */
  631.  
  632. static void
  633. RevCommand(iPtr, string)
  634.     register Interp *iPtr;    /* Interpreter in which to perform the
  635.                  * substitution. */
  636.     char *string;        /* String to substitute. */
  637. {
  638.     register HistoryRev *revPtr;
  639.  
  640.     if ((iPtr->evalFirst == NULL) || (iPtr->revDisables > 0)) {
  641.     return;
  642.     }
  643.     revPtr = (HistoryRev *) ckalloc(sizeof(HistoryRev));
  644.     revPtr->firstIndex = iPtr->evalFirst - iPtr->historyFirst;
  645.     revPtr->lastIndex = iPtr->evalLast - iPtr->historyFirst;
  646.     revPtr->newSize = strlen(string);
  647.     revPtr->newBytes = (char *) ckalloc((unsigned) (revPtr->newSize+1));
  648.     strcpy(revPtr->newBytes, string);
  649.     InsertRev(iPtr, revPtr);
  650. }
  651.  
  652. /*
  653.  *----------------------------------------------------------------------
  654.  *
  655.  * RevResult --
  656.  *
  657.  *    This procedure is invoked by the "history" command to record
  658.  *    a result revision.  See the comments at the beginning of the
  659.  *    file for more information about revisions.
  660.  *
  661.  * Results:
  662.  *    None.
  663.  *
  664.  * Side effects:
  665.  *    Revision information is recorded.
  666.  *
  667.  *----------------------------------------------------------------------
  668.  */
  669.  
  670. static void
  671. RevResult(iPtr, string)
  672.     register Interp *iPtr;    /* Interpreter in which to perform the
  673.                  * substitution. */
  674.     char *string;        /* String to substitute. */
  675. {
  676.     register HistoryRev *revPtr;
  677.     char *evalFirst, *evalLast;
  678.     char *argv[2];
  679.  
  680.     if ((iPtr->evalFirst == NULL) || (iPtr->revDisables > 0)) {
  681.     return;
  682.     }
  683.  
  684.     /*
  685.      * Expand the replacement range to include the brackets that surround
  686.      * the command.  If there aren't any brackets (i.e. this command was
  687.      * invoked at top-level) then don't do any revision.  Also, if there
  688.      * are several commands in brackets, of which this is just one,
  689.      * then don't do any revision.
  690.      */
  691.  
  692.     evalFirst = iPtr->evalFirst;
  693.     evalLast = iPtr->evalLast + 1;
  694.     while (1) {
  695.     if (evalFirst == iPtr->historyFirst) {
  696.         return;
  697.     }
  698.     evalFirst--;
  699.     if (*evalFirst == '[') {
  700.         break;
  701.     }
  702.     if (!isspace(*evalFirst)) {
  703.         return;
  704.     }
  705.     }
  706.     if (*evalLast != ']') {
  707.     return;
  708.     }
  709.  
  710.     revPtr = (HistoryRev *) ckalloc(sizeof(HistoryRev));
  711.     revPtr->firstIndex = evalFirst - iPtr->historyFirst;
  712.     revPtr->lastIndex = evalLast - iPtr->historyFirst;
  713.     argv[0] = string;
  714.     revPtr->newBytes = Tcl_Merge(1, argv);
  715.     revPtr->newSize = strlen(revPtr->newBytes);
  716.     InsertRev(iPtr, revPtr);
  717. }
  718.  
  719. /*
  720.  *----------------------------------------------------------------------
  721.  *
  722.  * DoRevs --
  723.  *
  724.  *    This procedure is called to apply the history revisions that
  725.  *    have been recorded in iPtr.
  726.  *
  727.  * Results:
  728.  *    None.
  729.  *
  730.  * Side effects:
  731.  *    The most recent entry in the history for iPtr may be modified.
  732.  *
  733.  *----------------------------------------------------------------------
  734.  */
  735.  
  736. static void
  737. DoRevs(iPtr)
  738.     register Interp *iPtr;    /* Interpreter whose history is to
  739.                  * be modified. */
  740. {
  741.     register HistoryRev *revPtr;
  742.     register HistoryEvent *eventPtr;
  743.     char *newCommand, *p;
  744.     unsigned int size;
  745.     int bytesSeen, count;
  746.  
  747.     if (iPtr->revPtr == NULL) {
  748.     return;
  749.     }
  750.  
  751.     /*
  752.      * The revision is done in two passes.  The first pass computes the
  753.      * amount of space needed for the revised event, and the second pass
  754.      * pieces together the _new event and frees up the revisions.
  755.      */
  756.  
  757.     eventPtr = &iPtr->events[iPtr->curEvent];
  758.     size = strlen(eventPtr->command) + 1;
  759.     for (revPtr = iPtr->revPtr; revPtr != NULL; revPtr = revPtr->nextPtr) {
  760.     size -= revPtr->lastIndex + 1 - revPtr->firstIndex;
  761.     size += revPtr->newSize;
  762.     }
  763.  
  764.     newCommand = (char *) ckalloc(size);
  765.     p = newCommand;
  766.     bytesSeen = 0;
  767.     for (revPtr = iPtr->revPtr; revPtr != NULL; ) {
  768.     HistoryRev *nextPtr = revPtr->nextPtr;
  769.  
  770.     count = revPtr->firstIndex - bytesSeen;
  771.     if (count > 0) {
  772.         strncpy(p, eventPtr->command + bytesSeen, count);
  773.         p += count;
  774.     }
  775.     strncpy(p, revPtr->newBytes, revPtr->newSize);
  776.     p += revPtr->newSize;
  777.     bytesSeen = revPtr->lastIndex+1;
  778.     ckfree(revPtr->newBytes);
  779.     ckfree((char *) revPtr);
  780.     revPtr = nextPtr;
  781.     }
  782.     if (&p[strlen(&eventPtr->command[bytesSeen]) + 1] >
  783.         &newCommand[size]) {
  784.     printf("Assertion failed!\n");
  785.     }
  786.     strcpy(p, eventPtr->command + bytesSeen);
  787.  
  788.     /*
  789.      * Replace the command in the event.
  790.      */
  791.  
  792.     ckfree(eventPtr->command);
  793.     eventPtr->command = newCommand;
  794.     eventPtr->bytesAvl = size;
  795.     iPtr->revPtr = NULL;
  796. }
  797.  
  798. /*
  799.  *----------------------------------------------------------------------
  800.  *
  801.  * GetEvent --
  802.  *
  803.  *    Given a textual description of an event (see the manual page
  804.  *    for legal values) find the corresponding event and return its
  805.  *    command string.
  806.  *
  807.  * Results:
  808.  *    The return value is a pointer to the event named by "string".
  809.  *    If no such event exists, then NULL is returned and an error
  810.  *    message is left in iPtr.
  811.  *
  812.  * Side effects:
  813.  *    None.
  814.  *
  815.  *----------------------------------------------------------------------
  816.  */
  817.  
  818. static HistoryEvent *
  819. GetEvent(iPtr, string)
  820.     register Interp *iPtr;    /* Interpreter in which to look. */
  821.     char *string;        /* Description of event. */
  822. {
  823.     int eventNum, index;
  824.     register HistoryEvent *eventPtr;
  825.     int length;
  826.  
  827.     /*
  828.      * First check for a numeric specification of an event.
  829.      */
  830.  
  831.     if (isdigit(*string) || (*string == '-')) {
  832.     if (Tcl_GetInt((Tcl_Interp *) iPtr, string, &eventNum) != TCL_OK) {
  833.         return NULL;
  834.     }
  835.     if (eventNum < 0) {
  836.         eventNum += iPtr->curEventNum;
  837.         }
  838.     if (eventNum > iPtr->curEventNum) {
  839.         Tcl_AppendResult((Tcl_Interp *) iPtr, "event \"", string,
  840.             "\" hasn't occurred yet", (char *) NULL);
  841.         return NULL;
  842.     }
  843.     if ((eventNum <= iPtr->curEventNum-iPtr->numEvents)
  844.         || (eventNum <= 0)) {
  845.         Tcl_AppendResult((Tcl_Interp *) iPtr, "event \"", string,
  846.             "\" is too far in the past", (char *) NULL);
  847.         return NULL;
  848.     }
  849.     index = iPtr->curEvent + (eventNum - iPtr->curEventNum);
  850.     if (index < 0) {
  851.         index += iPtr->numEvents;
  852.     }
  853.     return &iPtr->events[index];
  854.     }
  855.  
  856.     /*
  857.      * Next, check for an event that contains the string as a prefix or
  858.      * that matches the string in the sense of Tcl_StringMatch.
  859.      */
  860.  
  861.     length = strlen(string);
  862.     for (index = iPtr->curEvent - 1; ; index--) {
  863.     if (index < 0) {
  864.         index += iPtr->numEvents;
  865.     }
  866.     if (index == iPtr->curEvent) {
  867.         break;
  868.     }
  869.     eventPtr = &iPtr->events[index];
  870.     if ((strncmp(eventPtr->command, string, length) == 0)
  871.         || Tcl_StringMatch(eventPtr->command, string)) {
  872.         return eventPtr;
  873.     }
  874.     }
  875.  
  876.     Tcl_AppendResult((Tcl_Interp *) iPtr, "no event matches \"", string,
  877.         "\"", (char *) NULL);
  878.     return NULL;
  879. }
  880.  
  881. /*
  882.  *----------------------------------------------------------------------
  883.  *
  884.  * SubsAndEval --
  885.  *
  886.  *    Generate a _new command by making a textual substitution in
  887.  *    the "cmd" argument.  Then execute the _new command.
  888.  *
  889.  * Results:
  890.  *    The return value is a standard Tcl error.
  891.  *
  892.  * Side effects:
  893.  *    History gets revised if the substitution is occurring on
  894.  *    a recorded command line.  Also, the re-executed command
  895.  *    may produce side-effects.
  896.  *
  897.  *----------------------------------------------------------------------
  898.  */
  899.  
  900. static int
  901. SubsAndEval(iPtr, cmd, old, _new)
  902.     register Interp *iPtr;    /* Interpreter in which to execute
  903.                  * _new command. */
  904.     char *cmd;            /* Command in which to substitute. */
  905.     char *old;            /* String to search for in command. */
  906.     char *_new;            /* Replacement string for "old". */
  907. {
  908.     char *src, *dst, *newCmd;
  909.     int count, oldLength, newLength, length, result;
  910.  
  911.     /*
  912.      * Figure out how much space it will take to hold the
  913.      * substituted command (and complain if the old string
  914.      * doesn't appear in the original command).
  915.      */
  916.  
  917.     oldLength = strlen(old);
  918.     newLength = strlen(_new);
  919.     src = cmd;
  920.     count = 0;
  921.     while (1) {
  922.     src = strstr(src, old);
  923.     if (src == NULL) {
  924.         break;
  925.     }
  926.     src += oldLength;
  927.     count++;
  928.     }
  929.     if (count == 0) {
  930.     Tcl_AppendResult((Tcl_Interp *) iPtr, "\"", old,
  931.         "\" doesn't appear in event", (char *) NULL);
  932.     return TCL_ERROR;
  933.     }
  934.     length = strlen(cmd) + count*(newLength - oldLength);
  935.  
  936.     /*
  937.      * Generate a substituted command.
  938.      */
  939.  
  940.     newCmd = (char *) ckalloc((unsigned) (length + 1));
  941.     dst = newCmd;
  942.     while (1) {
  943.     src = strstr(cmd, old);
  944.     if (src == NULL) {
  945.         strcpy(dst, cmd);
  946.         break;
  947.     }
  948.     strncpy(dst, cmd, src-cmd);
  949.     dst += src-cmd;
  950.     strcpy(dst, _new);
  951.     dst += newLength;
  952.     cmd = src + oldLength;
  953.     }
  954.  
  955.     RevCommand(iPtr, newCmd);
  956.     result = Tcl_Eval((Tcl_Interp *) iPtr, newCmd, 0, (char **) NULL);
  957.     ckfree(newCmd);
  958.     return result;
  959. }
  960.  
  961. /*
  962.  *----------------------------------------------------------------------
  963.  *
  964.  * GetWords --
  965.  *
  966.  *    Given a command string, return one or more words from the
  967.  *    command string.
  968.  *
  969.  * Results:
  970.  *    The return value is a pointer to a dynamically-allocated
  971.  *    string containing the words of command specified by "words".
  972.  *    If the word specifier has improper syntax then an error
  973.  *    message is placed in iPtr->result and NULL is returned.
  974.  *
  975.  * Side effects:
  976.  *    Memory is allocated.  It is the caller's responsibilty to
  977.  *    free the returned string..
  978.  *
  979.  *----------------------------------------------------------------------
  980.  */
  981.  
  982. static char *
  983. GetWords(iPtr, command, words)
  984.     register Interp *iPtr;    /* Tcl interpreter in which to place
  985.                  * an error message if needed. */
  986.     char *command;        /* Command string. */
  987.     char *words;        /* Description of which words to extract
  988.                  * from the command.  Either num[-num] or
  989.                  * a pattern. */
  990. {
  991.     char *result;
  992.     char *start, *end, *dst;
  993.     register char *next;
  994.     int first;            /* First word desired. -1 means last word
  995.                  * only. */
  996.     int last;            /* Last word desired.  -1 means use everything
  997.                  * up to the end. */
  998.     int index;            /* Index of current word. */
  999.     char *pattern;
  1000.  
  1001.     /*
  1002.      * Figure out whether we're looking for a numerical range or for
  1003.      * a pattern.
  1004.      */
  1005.  
  1006.     pattern = NULL;
  1007.     first = 0;
  1008.     last = -1;
  1009.     if (*words == '$') {
  1010.     if (words[1] != '\0') {
  1011.         goto error;
  1012.     }
  1013.     first = -1;
  1014.     } else if (isdigit(*words)) {
  1015.     first = strtoul(words, &start, 0);
  1016.     if (*start == 0) {
  1017.         last = first;
  1018.     } else if (*start == '-') {
  1019.         start++;
  1020.         if (*start == '$') {
  1021.         start++;
  1022.         } else if (isdigit(*start)) {
  1023.         last = strtoul(start, &start, 0);
  1024.         } else {
  1025.         goto error;
  1026.         }
  1027.         if (*start != 0) {
  1028.         goto error;
  1029.         }
  1030.     }
  1031.     if ((first > last) && (last != -1)) {
  1032.         goto error;
  1033.     }
  1034.     } else {
  1035.     pattern = words;
  1036.     }
  1037.  
  1038.     /*
  1039.      * Scan through the words one at a time, copying those that are
  1040.      * relevant into the result string.  Allocate a result area large
  1041.      * enough to hold all the words if necessary.
  1042.      */
  1043.  
  1044.     result = (char *) ckalloc((unsigned) (strlen(command) + 1));
  1045.     dst = result;
  1046.     for (next = command; isspace(*next); next++) {
  1047.     /* Empty loop body:  just find start of first word. */
  1048.     }
  1049.     for (index = 0; *next != 0; index++) {
  1050.     start = next;
  1051.     end = TclWordEnd(next, 0);
  1052.     for (next = end; isspace(*next); next++) {
  1053.         /* Empty loop body:  just find start of next word. */
  1054.     }
  1055.     if ((first > index) || ((first == -1) && (*next != 0))) {
  1056.         continue;
  1057.     }
  1058.     if ((last != -1) && (last < index)) {
  1059.         continue;
  1060.     }
  1061.     if (pattern != NULL) {
  1062.         int match;
  1063.         char savedChar = *end;
  1064.  
  1065.         *end = 0;
  1066.         match = Tcl_StringMatch(start, pattern);
  1067.         *end = savedChar;
  1068.         if (!match) {
  1069.         continue;
  1070.         }
  1071.     }
  1072.     if (dst != result) {
  1073.         *dst = ' ';
  1074.         dst++;
  1075.     }
  1076.     strncpy(dst, start, (end-start));
  1077.     dst += end-start;
  1078.     }
  1079.     *dst = 0;
  1080.  
  1081.     /*
  1082.      * Check for an out-of-range argument index.
  1083.      */
  1084.  
  1085.     if ((last >= index) || (first >= index)) {
  1086.     ckfree(result);
  1087.     Tcl_AppendResult((Tcl_Interp *) iPtr, "word selector \"", words,
  1088.         "\" specified non-existent words", (char *) NULL);
  1089.     return NULL;
  1090.     }
  1091.     return result;
  1092.  
  1093.     error:
  1094.     Tcl_AppendResult((Tcl_Interp *) iPtr, "bad word selector \"", words,
  1095.         "\":  should be num-num or pattern", (char *) NULL);
  1096.     return NULL;
  1097. }
  1098.